﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using DDNY.WebPortals.Common;
using System.ServiceModel.Syndication;
using System.Text;
using System.IO;


namespace FeedAgregator
{
    public class Agregator
    {

        public static void StartBackgroundThread(ThreadStart threadStart)
        {
            if (threadStart != null)
            {
                Thread thread = new Thread(threadStart);
                thread.IsBackground = true;
                thread.Name = "Aggregate";
                thread.Start();

            }
        }

        public static void StartAggregate()
        {
            StartBackgroundThread(delegate { Aggregate(); });
        }

        public static void Aggregate()
        {
            //            TransactionOptions option = new TransactionOptions()
            //{
            //    IsolationLevel =
            //    System.Transactions.IsolationLevel.ReadCommitted
            //};

            //            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, option))
            //            {
            try
            {
                using (FeedSignalDataContext ctx = new FeedSignalDataContext())
                {
                    var q = from status in ctx.Status
                            select status;
                    Status currentStatus = q.Single();
                    bool shouldBeForcedToProcess = CheckShoudBeForcedToProcess(currentStatus);

                    if (currentStatus.Started == 0 || shouldBeForcedToProcess)
                    {
                        Debug.WriteLine("starting...");
                        currentStatus.Started = 1;
                        currentStatus.LastUpdate = DateTime.Now;
                        ctx.SubmitChanges();

                        var categories = from cats in ctx.FeedCategories
                                         select cats;
                        foreach (FeedCategory catItem in categories)
                        {
                            bool shouldBeProcessed = CheckShoudBeProcessed(catItem);
                            #region process Category
                            if (shouldBeProcessed)
                            {
                                ProcessCategory(catItem, ctx);

                            }
                            #endregion
                        }
                        currentStatus.Started = 0;
                        ctx.SubmitChanges();
                        Debug.WriteLine("ending...");
                    }


                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("starting..." + ex.Message);

            }
            //    scope.Complete();
            //}
        }

        private static void ProcessCategory(FeedCategory catItem, FeedSignalDataContext ctx)
        {
            CategoryLog catLogToAdd = new CategoryLog();
            catLogToAdd.CategoryID = catItem.ID;
            catLogToAdd.StartedAt = DateTime.Now;
            ctx.CategoryLogs.InsertOnSubmit(catLogToAdd);
            ctx.SubmitChanges();
            Console.WriteLine(string.Format("Processing category:{0}", catItem.CategoryName));

            catItem.LastUpdate = DateTime.Now;
            ctx.SubmitChanges();

            var feedsInCategory = from feeds in ctx.Feeds
                                  where feeds.CategoryID == catItem.ID
                                  select feeds;
            #region processing feeds
            foreach (Feed feedItem in feedsInCategory)
            {
                bool feedShouldBeProcessed = CheckShoudBeProcessed(feedItem);
                if (feedShouldBeProcessed)
                {
                    ProcessFeed(feedItem, ctx);
                }
            }
            #endregion
            catLogToAdd.FinishedAt = DateTime.Now;
            ctx.SubmitChanges();

        }

        private static void ProcessFeed(Feed feedItem, FeedSignalDataContext ctx)
        {
            FeedLog feedLogToAdd = new FeedLog();
            feedLogToAdd.FeedID = feedItem.ID;
            feedLogToAdd.StartedAt = DateTime.Now;
            ctx.FeedLogs.InsertOnSubmit(feedLogToAdd);
            ctx.SubmitChanges();

            Console.WriteLine(string.Format("Processing feed:{0}", feedItem.FeedUrl));
            feedItem.LastCheck = DateTime.Now;
            ctx.SubmitChanges();
            try
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ProhibitDtd = false;

                XmlReader rdr = XmlReader.Create(feedItem.FeedUrl, settings);

                SyndicationFeed feed = SyndicationFeed.Load(rdr);
                rdr.Read();

                //RobustXmlReader rdr = RobustXmlReader. (feedItem.FeedUrl);
                //SyndicationFeed feed = SyndicationFeed.Load(rdr);
                //rdr.Read();
                foreach (SyndicationItem nodeItem in feed.Items)
                {
                    ProcessFeedItem(nodeItem, feedItem, ctx, feedLogToAdd);
                }
                ctx.SubmitChanges();
                if (feedLogToAdd.ItemsAdded > 0)
                {
                    if (feedItem.UpdateIntervalInMinutes > 10)
                    {
                        feedItem.UpdateIntervalInMinutes -= feedItem.UpdateIntervalInMinutes * 10 / 100;
                    }
                    else
                    {
                        feedItem.UpdateIntervalInMinutes = 10;
                    }

                }
                else
                {
                    if (feedItem.UpdateIntervalInMinutes < 1200)
                    {
                        feedItem.UpdateIntervalInMinutes += feedItem.UpdateIntervalInMinutes * 10 / 100;
                    }
                    else
                    {
                        feedItem.UpdateIntervalInMinutes = 1200;
                    }

                }
                feedLogToAdd.FinishedAt = DateTime.Now;
            }
            catch (Exception)
            {
                feedLogToAdd.FinishedAt = new DateTime(2000, 1, 1);
            }


            ctx.SubmitChanges();

        }

        private static void ProcessFeedItem(SyndicationItem nodeItem, Feed feedItem, FeedSignalDataContext ctx, FeedLog feedLogToAdd)
        {
            FeedItem itemToAdd = new FeedItem();
            if (nodeItem.Links != null && nodeItem.Links.Count > 0)
            {
                itemToAdd.ItemUrl = nodeItem.Links[0].Uri.AbsoluteUri;
            }

            itemToAdd.Title = nodeItem.Title.Text;


            //itemToAdd.ItemContent = GetContents(nodeItem);
            itemToAdd.ItemContent = nodeItem.Summary.Text;
            itemToAdd.FeedGuid = nodeItem.Id;
            itemToAdd.PubDate = nodeItem.PublishDate.DateTime;
            itemToAdd.FeedID = feedItem.ID;
            bool itemIsFine = CheckItemForBadWords(itemToAdd);
            itemToAdd.ItemHash = itemToAdd.ItemUrl.GetHashCode().ToString();
            if (itemToAdd.PubDate == DateTime.MinValue)
            {
                itemToAdd.PubDate = DateTime.Now;
            }

            //if (itemToAdd.PubDate==DateTime.MinValue)
            //{

            //    string pubDate = nodeItem.SelectSingleNode("pubDate").InnerText;

            //    pubDate = pubDate.Replace("EEST", "+0200");
            //    pubDate = pubDate.Replace("MET", "+0200");
            //    pubDate = pubDate.Replace("EET", "+0200");
            //    pubDate = pubDate.Replace("PST", "+0200");
            //    pubDate = pubDate.Replace("GMT", "+0200");

            //    DateTime pubDateTime;
            //    bool parsed = DateTime.TryParse(pubDate, out pubDateTime);
            //    if (parsed == true)
            //    {
            //        itemToAdd.PubDate = pubDateTime;
            //    }
            //    else
            //    {
            //        parsed = DateTime.TryParseExact(pubDate, "yyyy-MM-dd HH:mm:ss", null, System.Globalization.DateTimeStyles.AdjustToUniversal, out pubDateTime);
            //        if (parsed == true)
            //        {
            //            itemToAdd.PubDate = pubDateTime;
            //        }
            //        else
            //        {
            //            itemToAdd.PubDate = DateTime.Now;
            //        }
            //    }
            //    if (itemToAdd.PubDate.CompareTo(DateTime.Now) > 0)
            //    {
            //        itemToAdd.PubDate = DateTime.Now;
            //    }    
            //}




            var sameTitleItems = from items in ctx.FeedItems
                                 where
                                 items.Title == itemToAdd.Title
                                 &&
                                 items.PubDate.Date == itemToAdd.PubDate.Date
                                 select items;
            if (!sameTitleItems.Any())
            {
                var existingItems = from feedItems in ctx.FeedItems
                                    where
                                        feedItems.ItemUrl == itemToAdd.ItemUrl
                                        || (feedItems.FeedGuid == itemToAdd.FeedGuid && itemToAdd.FeedGuid != null)
                                    select feedItems;
                if (existingItems.Any())
                {
                    foreach (FeedItem oldFeedItem in existingItems)
                    {
                        oldFeedItem.Title = itemToAdd.Title;
                        oldFeedItem.ItemUrl = itemToAdd.ItemUrl;
                        oldFeedItem.FeedID = itemToAdd.FeedID;
                        if (itemToAdd.ItemContent != null)
                        {
                            oldFeedItem.ItemContent = itemToAdd.ItemContent;
                        }
                        //oldFeedItem.PubDate = itemToAdd.PubDate;
                        feedLogToAdd.ItemsUpdated += 1;
                    }
                    ctx.SubmitChanges();
                }
                else
                {
                    itemToAdd.SEOUrl = CreateUniqueSeoUrl(itemToAdd.Title, ctx);
                    ctx.FeedItems.InsertOnSubmit(itemToAdd);
                    feedLogToAdd.ItemsAdded += 1;
                    ctx.SubmitChanges();
                    ProcessTagsForItem(itemToAdd, ctx);
                    ProcessCategoriesForItem(nodeItem, ctx, itemToAdd);
                }
            }

        }

        private static string GetContents(SyndicationItem nodeItem)
        {
            int maxLength = 100;
            StringBuilder result = new StringBuilder();
            string contentString = GetContentString(nodeItem.Summary.Text);

            return result.ToString();
        }

        private static string GetContentString(string content)
        {
            string result = string.Empty;
            int maxLength = 100;
            if (content.Length > maxLength)
            {
                int lastIndexOfSpace = content.IndexOf(' ', maxLength - 10);
                int cuttingLength;
                if (lastIndexOfSpace < maxLength)
                {
                    cuttingLength = lastIndexOfSpace;
                }
                else
                {
                    cuttingLength = maxLength;
                }

                result = content.Substring(cuttingLength);
            }
            return result;
        }

        private static void ProcessCategoriesForItem(SyndicationItem nodeItem, FeedSignalDataContext ctx, FeedItem itemToAdd)
        {
            if (nodeItem.Categories != null)
            {
                foreach (SyndicationCategory category in nodeItem.Categories)
                {
                    //checking for numeric categories
                    Regex _isNumber = new Regex(@"^\d+$");
                    Match m = _isNumber.Match(category.Name);
                    if (!m.Success)
                    {
                        int categoryId;
                        var existingCategory = from categories in ctx.FeedItemCategories
                                               where categories.CategoryName == category.Name
                                               select categories;
                        if (existingCategory.Any())
                        {
                            categoryId = existingCategory.Single().ID;
                        }
                        else
                        {
                            FeedItemCategory categoryToAdd = new FeedItemCategory();
                            categoryToAdd.CategoryName = category.Name;
                            ctx.FeedItemCategories.InsertOnSubmit(categoryToAdd);
                            ctx.SubmitChanges();
                            categoryId = categoryToAdd.ID;
                        }
                        try
                        {
                            FeedItemCategoryRelation relationToAdd = new FeedItemCategoryRelation();
                            relationToAdd.CategoryID = categoryId;
                            relationToAdd.FeedItemID = itemToAdd.ID;
                            var existingRelation = from relations in ctx.FeedItemCategoryRelations
                                                   where relations.CategoryID == categoryId &&
                                                   relations.FeedItemID == itemToAdd.ID
                                                   select relations;
                            if (!existingRelation.Any())
                            {
                                ctx.FeedItemCategoryRelations.InsertOnSubmit(relationToAdd);
                                ctx.SubmitChanges();
                            }
                        }
                        catch (Exception ex)
                        {


                        }

                    }
                }
            }
        }

        private static string CreateUniqueSeoUrl(string itemTitle, FeedSignalDataContext ctx)
        {
            string result;
            itemTitle = itemTitle.Replace(":", "-");
            itemTitle = itemTitle.Replace("?", "");
            itemTitle = itemTitle.Replace("'", "");
            itemTitle = itemTitle.Replace(@"""", "");
            if (itemTitle.Length > 200)
            {
                itemTitle = itemTitle.Substring(0, 200);
            }
            string tempResult = itemTitle.ToLower();
            tempResult = Transliterator.FromCyrToLatin(tempResult);

            var query = from feedItems in ctx.FeedItems
                        where feedItems.SEOUrl.StartsWith(tempResult)
                        select feedItems;
            int similarTitlesCount = query.Count();
            if (similarTitlesCount > 0)
            {
                tempResult += "-" + (similarTitlesCount + 1).ToString();
            }
            result = tempResult;
            return result;
        }



        private static void ProcessTagsForItem(FeedItem itemToAdd, FeedSignalDataContext ctx)
        {

            List<Tag> tagList = (from tags in ctx.Tags
                                 select tags).ToList();
            Dictionary<int, FeedItemTagRelation> tagRelationsToAdd = new Dictionary<int, FeedItemTagRelation>();
            foreach (Tag tagItem in tagList)
            {
                if (itemToAdd.Title.Contains(tagItem.TagName))
                {
                    FeedItemTagRelation relationToAdd;
                    relationToAdd = new FeedItemTagRelation();
                    relationToAdd.FeedItemID = itemToAdd.ID;
                    relationToAdd.TagID = tagItem.ID;
                    relationToAdd.TimesMet = CountStringOccurrences(itemToAdd.Title, tagItem.TagName);
                    tagRelationsToAdd.Add(tagItem.ID, relationToAdd);
                }
            }

            ctx.FeedItemTagRelations.InsertAllOnSubmit(tagRelationsToAdd.Values);
            ctx.SubmitChanges();
        }

        public static int CountStringOccurrences(string text, string pattern)
        {
            // Loop through all instances of the string 'text'.
            int count = 0;
            int i = 0;
            while ((i = text.IndexOf(pattern, i)) != -1)
            {
                i += pattern.Length;
                count++;
            }
            return count;
        }


        public static void ProcessTagsForOldFeedItems()
        {
            using (FeedSignalDataContext ctx = new FeedSignalDataContext())
            {
                ctx.ExecuteCommand("truncate table FeedItemTagRelation");
                ctx.SubmitChanges();
                var query = from feedItems in ctx.FeedItems
                            select feedItems;
                List<FeedItem> allItems = query.ToList();
                foreach (FeedItem item in allItems)
                {
                    ProcessTagsForItem(item, ctx);
                }
            }
        }

        public static void ProcessSeoUrlsForOldItems()
        {
            using (FeedSignalDataContext ctx = new FeedSignalDataContext())
            {
                var query = from feedItems in ctx.FeedItems
                            where feedItems.SEOUrl == null
                            orderby feedItems.PubDate ascending
                            select feedItems;
                foreach (FeedItem item in query)
                {
                    if (item.SEOUrl == null)
                    {


                        item.SEOUrl = CreateUniqueSeoUrl(item.Title, ctx);
                        ctx.SubmitChanges();
                    }
                }
            }
        }

        private static bool CheckShoudBeProcessed(Feed feedItem)
        {
            bool result;
            if (feedItem.LastCheck == null)
            {
                result = true;
            }
            else
            {
                TimeSpan timeSinceLastUpdate = DateTime.Now.Subtract(feedItem.LastCheck.Value);
                double minutesSinceLastUpdate = timeSinceLastUpdate.TotalMinutes;
                if (minutesSinceLastUpdate > feedItem.UpdateIntervalInMinutes)
                {
                    result = true;
                }
                else
                {
                    result = false;
                }
            }
            return result;
        }

        private static bool CheckItemForBadWords(FeedItem itemToAdd)
        {
            bool result = true;
            //TODO
            return result;
        }

        private static bool CheckShoudBeProcessed(FeedCategory catItem)
        {
            bool result;
            if (catItem.LastUpdate == null)
            {
                result = true;
            }
            else
            {
                TimeSpan timeSinceLastUpdate = DateTime.Now.Subtract(catItem.LastUpdate.Value);
                double minutesSinceLastUpdate = timeSinceLastUpdate.TotalMinutes;
                if (minutesSinceLastUpdate > catItem.UpdateIntervalInMinutes)
                {
                    result = true;
                }
                else
                {
                    result = false;
                }
            }
            return result;
        }


        private static bool CheckShoudBeForcedToProcess(Status status)
        {
            bool result;
            if (status.LastUpdate == null)
            {
                result = true;
            }
            else
            {
                TimeSpan timeSinceLastUpdate = DateTime.Now.Subtract(status.LastUpdate.Value);
                double minutesSinceLastUpdate = timeSinceLastUpdate.TotalMinutes;
                if (minutesSinceLastUpdate > status.MaxIntervalInMinutes)
                {
                    result = true;
                }
                else
                {
                    result = false;
                }
            }
            return result;
        }
    }


}
